package com.rongji.cms.tools.common.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rongji.cms.tools.common.annotation.NotControllerResponseAdvice;
import com.rongji.cms.tools.common.enums.CommonResultCode;
import com.rongji.cms.tools.common.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Arrays;
import java.util.List;

/**
 * 全局响应预处理
 *
 * @author yang
 * @date 2022/07/04
 */
@Slf4j
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
    // FIXME: 2022/11/16 换springdoc了，放行的uri也得改下
//也可以用这种方式，通过uri判断
    private List<String> EXCLUDE_URI = Arrays.asList(
            "/swagger-ui/**",
            "/favicon.ico",
            "/*/api-docs",
            "/*/api-docs/**",
//            spring-boot-admin监控的也要过滤
            "/actuator",
            "/actuator/**",
//            hippo4j-server监控
            "/web/**",
            "/run/**"
    );
//    旧的swagger的
//    private List<String> EXCLUDE_URI = Arrays.asList(
//            "/doc.html",
//            "/swagger-resources",
//            "/swagger-resources/configuration",
//            "/v3/api-docs",
//            "/v2/api-docs",
//            "/webjars/**");

    /** 调试一下看看methodParameter对应的swagger是什么类型，然后添加在数组中 */
//    private static final String[] EXCLUDE = {
//            "Swagger2Controller",
//            "Swagger2ControllerWebMvc",
//            "ApiResourceController",
//            //		modified by yang 2022-10-18   spring-boot-admin监控的也要过滤
//            "OperationHandler"
//    };

    /**
     * 支持
     *
     * @param methodParameter 方法参数
     * @param aClass          一个类
     * @return boolean
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        // response是ResultVo类型，或者注释了NotControllerResponseAdvice都不进行包装
        return !(methodParameter.getParameterType().isAssignableFrom(ResultVO.class)
                || methodParameter.hasMethodAnnotation(NotControllerResponseAdvice.class));
    }

    /**
     * 在返回值写入之前，做一些预处理
     *
     * @param o                     o
     * @param returnType            返回类型
     * @param selectedContentType   所选内容类型
     * @param selectedConverterType 选择转换类型
     * @param request               请求
     * @param response              响应
     * @return {@link Object}
     */
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
//        log.info("before body write param:{}", o);
//        fixme 这里要特殊处理string类型，要将整个ResultVO转成string
//        参考链接 https://blog.csdn.net/weixin_45176654/article/details/109689869
//        String simpleName = returnType.getDeclaringClass().getSimpleName();
//        String path = request.getURI().getPath();
        //先判断例外
        if (isExcludeUrl(request.getURI().getPath())) {
            //如果是接口文档的uri，直接跳过
            return o;
        } else if (o instanceof String) {
            ObjectMapper mapper = new ObjectMapper();
            try {
                return mapper.writeValueAsString(ResultVO.success(o));
            } catch (JsonProcessingException e) {
                log.error(e.getMessage(), e);
//                fixme 尽量不要直接 e.printStackTrace()
//                e.printStackTrace();
            }

        }

        // 避免swagger失效
//        else if (Arrays.asList(EXCLUDE).contains(returnType.getDeclaringClass().getSimpleName())) {
//            return o;
//        }


//        如果本身就是ResultVO类型，就直接返回
        else if (o instanceof ResultVO) {
            return o;
        }

//       其他的就包装下ResultVO
        return new ResultVO(CommonResultCode.SUCCESS.getCode(), CommonResultCode.SUCCESS.getMsg(), o);
    }

    private boolean isExcludeUrl(String uri) {
        AntPathMatcher pathMatcher = new AntPathMatcher();
        for (String s : EXCLUDE_URI) {
            if (pathMatcher.match(s, uri)) {
                return true;
            }
        }
        return false;
    }

}
